
import fake.ELoopMgmtException;
import fake.ENextException;
import fake.EStopException;
import symbols.SLeftValue;


class HL {

    private static SimpleCharStream instream;
    private static ParserTokenManager scanner;
    private static Parser parser;
    private static boolean DEBUG = false;	// will depend on property passed on CLI

    public static void main(String args[]) {
    	// activate or not debugging from user input
    	if(System.getProperty("DEBUG") != null) {
    		DEBUG = true;
    		System.out.println("stack trace");
    	}

		// Figure out input stream from args or stdin
		java.io.InputStream infile;
		if (args.length < 1) {
	  	infile = System.in;
		} 
		else try {  
	   	infile = new java.io.FileInputStream(args[0]);
		} catch (java.io.FileNotFoundException e) {
	    	System.out.println("File " + args[0] + " not found."); 
	    	return;
		} 

		// Create scanner and parser
		instream = new SimpleCharStream(infile);
		scanner = new ParserTokenManager(instream);
		parser = new Parser(scanner);

		// Call parser until EOF reached
		while (nextParseSkipErrors());
    	}


    private static boolean nextParseSkipErrors() {
		SimpleNode tree;
		Eval evaluator = new Eval();
		try {
			Parser.ResetErrors();
			tree = parser.start();
			if (Parser.AnyErrors())
				; //System.out.println("Parsing failed");	for assignment 3 part 3
			else {
				evaluator.setSymbolTable(Parser.symbolTable);
				Object result = tree.jjtAccept(evaluator,null);
				if(result != null	// do not print anything for statement
						&& ! (result instanceof SLeftValue && ("" + result).equals("null"))) // below extract from email Mon, December 11, 2006 12:52 pm
																										// "the null part displays nothing in mine.  The semantics were not 
																										// defined.  If you want to display "null", you won't be penalized."
																										// let's do it anyway!
																										// a null thrown here is caught in HL and not displayed (if null)
					System.out.println(result);
				System.out.flush();
			}

		// Handle Semantic Errors which can be caught during parsing
		} catch(ESemanticException e) {
			displaySemanticError(e);
			
		// Handle Semantic Errors which can be caught during parsing
		} catch(ERuntimeException e) {
			displayRuntimeError(e);	
			
		} catch(ELoopMgmtException e) {
			displayLoopException(e);
			
		// Handle Too Many Syntax Errors and EOF
		} catch (ParseAbortException e) {
			if (e.isEOF())	return false;
	    	System.out.println(e.toString());
	    	
		} catch (ParseException e) {
				/* Dummy: ParseException will be caught by start().
				 * However, JavaCC nevertheless automatically adds a "throws ParseException"
				 * to the function prototype of start().
				 * Therefore nextParseSkipErrors() must be seen to catch this exception		*/
			
		} catch (Exception e) {
			System.out.println("Weird unexpected exception");
			if(DEBUG) e.printStackTrace();
		}
		return true;
	}


	private static void displayLoopException(ELoopMgmtException e) {
		System.out.print("Semantic error in loop: ");
		
		if(e instanceof ENextException)
			System.out.println("next " + e.getValue() + " has no matching loop.");
		
		if(e instanceof EStopException)
			System.out.println("stop " + e.getValue() + " has no matching loop.");		
	}


	private static void displaySemanticError(ESemanticException e) {
		System.out.print("Semantic error: " + e.message + " ");
		
		if(e instanceof EUndefinedIdentifierException) 
			System.out.println("Undefined Identifier");
		
		else if(e instanceof EIdentifierIsAlreadyDefinedException)			
			if(e instanceof ECallableIdentifierIsAlreadyDefinedException)
				System.out.println("This identifier has already been defined in the present scope");
			else
				System.out.println("This identifier has already been defined in the present scope");
		
		else if(e instanceof EUnsetIdentifierException)
			System.out.println("This variable has not been assigned a value");
		
		else if(e instanceof ETypeException) {
			if(e instanceof EInvalidTypeException)
				System.out.println("Invalid type");
			
			else if(e instanceof EUnknownTypeException)
				System.out.println("Type unknown");
			
			else if(e instanceof EIdentifierIsNotALeftValueException)
				System.out.println("Unable to assign value of uncompatible type");
		
		}else if(e instanceof EComparisonException) {
			if(e instanceof EInvalidTypeComparisonException)
				System.out.println("Unmatching data types");
			
			else if(e instanceof EUndefinedOperatorException)
				System.out.println("This comparison is not defined for the given value types");
			
		}else if(e instanceof EFunctionCallException) {
			if(e instanceof EMissingParametersException)
				System.out.println("Missing parameters in call");
			
			else if(e instanceof ETooManyParametersException)
				System.out.println("Too many parameters in call");
			
			else if(e instanceof EWrongParameterTypeException)
				System.out.println("Parameter type does not match");
			
		}else if(e instanceof EIllegalReturnStatementException)
					System.out.println("A subroutine does not have return statements");
		
		else if(e instanceof EReturnException) {
			if(e instanceof ESubReturnValueException)
				System.out.println("A subroutine does not return a value");
			
			else if(e instanceof EFunctionMustReturnAValue)
				System.out.println("A function must return a value");
			
			else if(e instanceof EFunctionCanNotReturnBooleanException)
				System.out.println("A function can not return a boolean value");
			
			else if(e instanceof EFunctionBadReturnTypeException)
				System.out.println("A function must return a value of the defined return type");
			
			else if(e instanceof EPredicateMustReturnABooleanException)
				System.out.println("A predicate must return a boolean value");
			
		}else if(e instanceof ELoopException) {
			if(e instanceof ELoopBadIdentifierTypeException)
				System.out.println("iterator in for loop must be of type num.");
			
			else if(e instanceof ELoopValueFromIsNotANumber)
				System.out.println("from value in for loop must be a number.");
			
			else if(e instanceof ELoopValueToMustBeANumberException)
				System.out.println("to value in for loop must be a number.");
			
			else if(e instanceof ELoopValueStepMustBeANumberException)
				System.out.println("step value in for loop must be a number.");
			
			else if(e instanceof ELoopImplicitIteratorMustBeOfTypeSTRException)
				System.out.println("for-in loop iterator over strings must be of type str.");
			
			else if(e instanceof ELoopImplicitIteratorMustBeOfTypeOBJException)
				System.out.println("for-in loop iterator over strings must be of type obj.");
			
			else if(e instanceof ELoopImplicitDoesOnlyStrOrSetException)
				System.out.println("for-in loop can only iterate in strings or sets.");	
			
		}
		
		if(DEBUG) e.printStackTrace();
	}


	private static void displayRuntimeError(ERuntimeException e) {
		System.out.print("Runtime error: " );
		
		if(e instanceof EOutOfBoundException)
			System.out.println("The value is out of range");
			
		else if(e instanceof ENumberOverflowException)
			System.out.println("Number overflow");		
			
		else if(e instanceof EDivisionByZeroException)
			System.out.println("Division by zero");			
			
		else if(e instanceof EModByZeroException)
			System.out.println("Mod by zero");		
		
		else
			System.out.println("please check the documentation");	// should not happen!
		
		if(DEBUG) e.printStackTrace();
	}
}
